﻿
Imports Microsoft.VisualBasic
Imports System
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Media
Imports System.Windows.Shapes
Imports System.Windows.Input
Imports System.Windows.Media.Animation
Imports System.Windows.Threading
Imports System.Timers
Imports System.Windows.Forms
Imports System.Drawing
Imports System.IO

Imports log4net

Imports VPager.Settings
Imports VPager.WebService
Imports System.Runtime.Serialization
Imports VPager.Enumerations


Namespace Display

    ''' <summary>
    ''' The PagerDisplay is the main program execution class housing most
    ''' of the command-and-control logic.  Program code control takes over
    ''' by responding to the Window_Loaded event and shuts down by responding
    ''' to the PagerDisplay_Closed event.
    ''' 
    ''' The main responsibilites of this class are:
    ''' 
    '''   * Intialize the pager system including creating and launching
    '''     background processes and intializing logging
    '''   * Rotate display (with visual fade effects) of all active page codes
    '''   * Respond to changes in the active page set and update display
    '''   * Respond to background tasks error events
    '''   * respond to Display Window close event and shutdown application
    ''' 
    ''' After initialization, the main repsonsibility of the class is to display
    ''' the active page codes as provided by the PageList object and to update
    ''' the System Status LED on main display so that it periodically blinks
    ''' to indicate that the software is still executing through its main control 
    ''' loop and not hung up.  This is all handled through the processPageRequest()
    ''' method and is implemented as a series of coordinated asynchronous state 
    ''' transtions with wait times in between.  The asynchronous implementation is
    ''' used so as not to block the main UI Thread between transtions so that the
    ''' application overall runs more smoothly with less CPU overhead. 
    ''' 
    ''' State transitions are managed through the pageStateHandler() method.
    ''' 
    ''' </summary>
    ''' <remarks>
    ''' At some point most the command-and-control code should be moved into 
    ''' another non-Window class. </remarks>
    Class PagerDisplay

        ' The log file Mark Stamp
        Private Const MARK_STAMP As String = "----- MARK -----"

        Private Const BLANK_PAGE As String = ""

        ' State Transition Values
        Private Const STATE_FADE_IN_DONE As Integer = 0
        Private Const STATE_FADE_OUT_DONE As Integer = 1
        Private Const STATE_PAGE_VIEW_DONE As Integer = 2
        Private Const STATE_LED_BLINK_DONE As Integer = 3

        ' -------------------------------------------------------------------------
        ' Member Variables
        ' -------------------------------------------------------------------------
        '
        ' Utility Variables
        '    Private mPageList As PageList
        Private mLog As ILog
        Private mCanContinue As Boolean
        Private mNextMark As Date
        Private mPageBlock As TextBlock
        Private mFadeInAnimation As DoubleAnimation
        Private mFadeOutAnimation As DoubleAnimation
        Private mNextPageState As Integer
        Private mLayoutMgr As LayoutManager
        Private mStatusMgr As StatusManager
        Private mViewTimer As DispatcherTimer
        Private mBlinkTimer As DispatcherTimer
        Private mController As Controller
        Private mCurrentPage As ParentPage
        Private mDisplayMode As MonitorMode = MonitorMode.UNKNOWN

        ' -------------------------------------------------------------------------
        ' Property Definitions
        ' -------------------------------------------------------------------------
        '
        Public ReadOnly Property PageBlock
            Get
                Return mPageBlock
            End Get
        End Property

        Public ReadOnly Property CurrentPage As ParentPage
            Get
                Return mCurrentPage
            End Get
        End Property

        Public ReadOnly Property DisplayMode As MonitorMode
            Get
                Return mDisplayMode
            End Get
        End Property

        ' -------------------------------------------------------------------------
        ' Main Code
        ' -------------------------------------------------------------------------

        Private Sub PagerDisplay_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Closed
            ' Close Window
            mLog.Info("Main Display Closed.")
            ' Shutdown background threads
            mCanContinue = False

            ' Remove ourself as listeners to external object events
            RemoveHandler mController.PageList.Changed, AddressOf pageListChangeHandler
            RemoveHandler mController.NetMonitor.StatusChanged, AddressOf networkChangeHandler

        End Sub

        Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
            ' Start the display
            start()
        End Sub

        Private Sub initialize()

            mController = Controller.getInstance()

            mPageBlock = FindName("tbkPage")

            mLog = LogManager.GetLogger(Me.GetType)
            mLog.Info("Pager Display Started.")

            ' Setup Text Fade animations used to transition visually between
            ' multiple page codes.
            Dim transTime As Double = SettingsManager.getValue(SettingKey.TRANSITION_TIME)
            Dim fadeTime As Double = transTime / 2

            mFadeInAnimation = New DoubleAnimation(0, 1, New Duration(TimeSpan.FromSeconds(fadeTime)))
            AddHandler mFadeInAnimation.Completed, AddressOf pageStateHandler

            mFadeOutAnimation = New DoubleAnimation(1, 0, New Duration(TimeSpan.FromSeconds(fadeTime)))
            AddHandler mFadeOutAnimation.Completed, AddressOf pageStateHandler

            ' Setup the page view timer that signals when the next page transition
            ' should take place.
            Dim pagePause As Integer = SettingsManager.getValue(SettingKey.PAGE_PAUSE)
            mViewTimer = New DispatcherTimer()
            mViewTimer.Interval = New TimeSpan(0, 0, pagePause) 'Hours, minutes, Seconds
            AddHandler mViewTimer.Tick, AddressOf pageStateHandler

            ' Setup the System Status LED Blink timer.
            mBlinkTimer = New DispatcherTimer()
            mBlinkTimer.Interval = New TimeSpan(2500000) '100-Nanosecond Units ~0.25 Secs
            AddHandler mBlinkTimer.Tick, AddressOf pageStateHandler

            ' Create our LayoutManager that will layout the main display (Me)
            mLayoutMgr = New LayoutManager(Me)

            ' Create the StatusManger which owns the indidivual StatusLED objects.
            mStatusMgr = New StatusManager()
            ' Associated the LED UI Elements owned by the LayoutManager with its
            ' control logic owned by the StatusManager.
            mStatusMgr.SystemStatus.setUIElement(mLayoutMgr.SystemLED)
            mStatusMgr.NetworkStatus.setUIElement(mLayoutMgr.NetworkLED)
            mStatusMgr.PageStatus.setUIElement(mLayoutMgr.PageLED)

            ' Perform initial Display Layout.
            mLayoutMgr.configureMainDisplay()
            If mLayoutMgr.IsUsingExternal Then
                mDisplayMode = MonitorMode.EXTERNAL
            Else
                mDisplayMode = MonitorMode.PRIMARY
            End If

            mCanContinue = True

            ' Add handlers so that change and error events can be responded to.
            AddHandler mController.PageList.Changed, AddressOf pageListChangeHandler


            AddHandler mController.NetMonitor.StatusChanged, AddressOf networkChangeHandler

            ' Seed our next mark time
            mNextMark = Date.Now.AddSeconds(
                SettingsManager.getValue(SettingKey.MARK_TIME))

            ' Turn on Network LED
            mStatusMgr.NetworkStatus.turnLedOn()

        End Sub

        Private Sub setPageCode(ByVal code As String)
            mPageBlock.Text = code
        End Sub

        ''' <summary>
        ''' This is main state transition control code implemented as an event handler.
        ''' In order to keep the UI responsive state transtions during the paging cycle
        ''' are implemented as asynchronous actions.  Each asychronous action indicates
        ''' its completion by firing an event which is handled by this method.  Prior to 
        ''' execuitng the asynch action the next state transition is identifed by setting
        ''' the mNextPageState member variable to one of the STATE_* value constants.
        ''' </summary>
        ''' <param name="sender">Not used. Required part of event handler signature.</param>
        ''' <param name="e">Not used. Required part of event handler signature.</param>
        ''' <remarks></remarks>
        Private Sub pageStateHandler(ByVal sender As Object, ByVal e As EventArgs)

            If mNextPageState = STATE_FADE_IN_DONE Then
                ' Need to wait 3 seconds (display time) then Fade Out
                waitForPageToDisplay()

            ElseIf mNextPageState = STATE_FADE_OUT_DONE Then
                Dim page = mController.PageList.nextDisplayableItem
                ' TODO: In future other types beside ParentPage will be displayable
                If page Is Nothing Then
                    clearDisplay()
                    waitForPageToDisplay()
                ElseIf (TypeOf page Is ParentPage) Then
                    mCurrentPage = CType(page, ParentPage)
                    setPageCode(mCurrentPage.Code)
                    fadePageIn()
                End If

            ElseIf mNextPageState = STATE_PAGE_VIEW_DONE Then
                ' Forcing the CommandManager to raise the RequerySuggested event
                ' This is required to place the timer back into the Dispatch Queue
                CommandManager.InvalidateRequerySuggested()
                mViewTimer.Stop()

                ' Take opportunity to blink the System Status LED on main display
                ' by turning it off briefly and using the Blink Timer to add delay
                mStatusMgr.SystemStatus.turnLedOff()
                mNextPageState = STATE_LED_BLINK_DONE
                mBlinkTimer.Start()

            ElseIf mNextPageState = STATE_LED_BLINK_DONE Then
                mBlinkTimer.Stop()
                processSystemTasks()
                processPageRequests()

            End If

        End Sub

        Private Sub fadePageIn()
            ' Fade Code into view
            mNextPageState = STATE_FADE_IN_DONE
            mPageBlock.BeginAnimation(UIElement.OpacityProperty, mFadeInAnimation)
        End Sub

        Private Sub fadePageOut()
            ' Fade Code out of view
            mNextPageState = STATE_FADE_OUT_DONE
            mPageBlock.BeginAnimation(UIElement.OpacityProperty, mFadeOutAnimation)
        End Sub

        Private Sub waitForPageToDisplay()
            If mCanContinue Then
                ' Activate the view timer to trigger next transition. Timer duration = view duration
                mNextPageState = STATE_PAGE_VIEW_DONE

                mStatusMgr.SystemStatus.turnLedOn()

                mViewTimer.Start()
            Else
                mLog.Info("Exiting main paging control loop.")
            End If

        End Sub


        Sub start()

            Try
                initialize()
                clearDisplay()
                processPageRequests()

            Catch ex As Exception
                If mLog IsNot Nothing Then
                    mLog.Fatal("Unhandled Exception caught. " & ex.Message & vbNewLine & ex.StackTrace.ToString)
                    mLog.Fatal("Exiting application.")
                End If
                Exit Sub

            End Try

            Exit Sub

        End Sub

        Sub processPageRequests()

            Dim page As PageItem

            If mCanContinue Then
                'dbgWriteCurrentPage()
                page = mController.PageList.peek
                If page IsNot Nothing Then
                    mStatusMgr.PageStatus.turnLedOn()
                    'page = mController.PageList.peek
                    If Not page.SameAs(mCurrentPage) Then
                        fadePageOut()
                    Else
                        mController.PageList.nextDisplayableItem()
                        waitForPageToDisplay()
                    End If
                Else
                    mCurrentPage = Nothing
                    clearDisplay()
                    waitForPageToDisplay()
                End If
            Else
                mLog.Info("Exiting main paging control loop.")
            End If

        End Sub

        Sub dbgWriteCurrentPage()
            Dim code As String
            If mCurrentPage Is Nothing Then
                code = "<NONE>"
            Else
                code = mCurrentPage.Code
            End If
            Debug.WriteLine("Current Page: " + code)
        End Sub

        Private Sub clearDisplay()
            setPageCode(BLANK_PAGE)
        End Sub

        Private Sub processSystemTasks()
            checkMark()
        End Sub

        Sub checkMark()
            ' TODO: This should probably be moved out of the Display control loop and into
            '       the main Controller somehow.
            If Date.Compare(Date.Now, mNextMark) > 0 Then
                mLog.Info(MARK_STAMP)
                mNextMark = Date.Now.AddSeconds(SettingsManager.getValue(SettingKey.MARK_TIME))
            End If

        End Sub

        Private Sub pageListChangeHandler(ByVal sender As Object, ByVal e As EventArgs)
            Dim list = CType(sender, PageList)
            Dim codes As String = "<EMPTY>"
            If Not list.isEmpty Then
                codes = list.toString()
            End If
            mLog.Info("Active Page Code List: " & codes)
        End Sub

        Private Sub networkChangeHandler(ByVal sender As NetworkMonitor, ByVal e As NetworkMonitor.NetworkEventArgs)
            ' Network Status Change
            ' Update Status LED and log change
            mStatusMgr.NetworkStatus.Status = e.Status
            mLog.Info(e.Message)
            mStatusMgr.NetworkStatus.turnLedOn()
        End Sub


        Public Sub New()

            ' This call is required by the designer.
            InitializeComponent()

            ' Add any initialization after the InitializeComponent() call.

        End Sub
    End Class

End Namespace
